package by.bsu.rfe.belianko.euniversity.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;

import by.bsu.rfe.belianko.euniversity.exceptions.ConnectionExceptions;

/**
 * Creates JDBC pool of connections.
 * 
 * @author Vitali_Belianko
 * 
 */
public final class ConnectionPool {

	private static final String CREATING_CONNECTION_POOL = "Creating Connection pool!";
	private static final String CANNOT_GET_CONNECTION_TO_DB = "Can not get db connection!\n";
	private static final String CANNOT_TAKE_CONNECTION_FROM_QUEUE = "Can not take connection from queue!\n";
	private static final String CANNOT_LOAD_DBDRIVER = "Can not load db driver!\n";
	private static final ConfigurationManager configurationManager = ConfigurationManager.getInstance();
	private static final BlockingQueue<Connection> connections = new LinkedBlockingQueue<Connection>();
	private static final Logger LOGGER = Logger.getLogger(ConnectionPool.class);
	private static ConnectionPool instance ;

	private ConnectionPool() throws ConnectionExceptions {

		LOGGER.debug(CREATING_CONNECTION_POOL);

		int amountOfConnections = Integer.parseInt(configurationManager.getProperty(ConfigurationManager.POOL_SIZE));

		try {
			Class.forName(configurationManager.getProperty(ConfigurationManager.DATABASE_DRIVER_NAME));
		} catch (ClassNotFoundException e) {
			LOGGER.error(e.getMessage());
			throw new ConnectionExceptions(CANNOT_LOAD_DBDRIVER, e);

		}
		for (int i = 0; i < amountOfConnections; i++) {
			try {
				Connection connection = DriverManager.getConnection(
						configurationManager.getProperty(ConfigurationManager.DATABASE_URL),
						configurationManager.getProperty(ConfigurationManager.DATABASE_LOGIN),
						configurationManager.getProperty(ConfigurationManager.DATABASE_PASSWORD));
				connections.add(connection);

			} catch (SQLException e) {
				LOGGER.error(e.getMessage());
				throw new ConnectionExceptions(CANNOT_GET_CONNECTION_TO_DB, e);

			}
		}
	}

	/**
	 * Take free connection from the queue.
	 * 
	 * @return connection
	 * @throws ConnectionExceptions
	 * 
	 */
	public Connection getConnection() throws ConnectionExceptions {
		Connection connection = null;

		try {
			connection = connections.take();

			LOGGER.debug("Took connection");

		} catch (InterruptedException e) {
			LOGGER.error(e.getMessage());
			throw new ConnectionExceptions(CANNOT_TAKE_CONNECTION_FROM_QUEUE, e);

		}

		return connection;
	}

	/**
	 * Release connection in the queue.
	 * 
	 * @param connection
	 */
	public void releaseConnection(Connection connection) {

		LOGGER.debug("Released connection");

		connections.add(connection);
	}

	/**
	 * Close connections in the queue.
	 * 
	 * @throws ConnectionExceptions
	 * 
	 */
	public static void close() {
		for (Connection connection : connections) {
			try {
				connection.close();

				LOGGER.debug("Closed connection");

			} catch (SQLException e) {
				LOGGER.error(e.getMessage());

			}
		}
	}

	public static ConnectionPool getInstance() throws ConnectionExceptions {
		if (instance == null) {
			instance = new ConnectionPool();
		}
		return instance;
	}

}
